<?php
/*
 *  $Id: index.php 711 2012-06-18 22:02:11Z k42b3.x@googlemail.com $
 *
 * This is an Amun demo application. The demo application showcases and explains
 * functionalities of Amun. The code is released under the GPLv3. More
 * informations about Amun at http://amun.phpsx.org.
 *
 * This is a newsreader app wich loads news from an remote amun instance defined
 * in the BASE_URL. It is also possible to add a comment for each news. It
 * showcases the basic functionalities of an app to authenticate, receive and
 * post data to Amun. If no CONFIG_FILE was written the app tries to establish
 * OAuth credentials to act on the users behalf. The credentials are written to
 * the CONFIG_FILE. Simply delete the CONFIG_FILE if you want to restart the
 * authentication process. By default the consumer key and consumer secret by
 * the demo application on amun.phpsx.org are used but it can be used by any
 * other amun instance with the news service installed.
 */


// configuration
define('CONFIG_FILE', '.newsreader.config');
define('BASE_URL', 'http://amun.phpsx.org');
define('CONSUMER_KEY', '022170e918332b13167e33b74f4588e19c1bae5d');
define('CONSUMER_SECRET', '82bab8310d0b3a70f1406a0e46f0690799d3b8fa');
define('XRDS_CACHE', '.xrds.cache');


// set include path and autoloader
set_include_path(get_include_path() . PATH_SEPARATOR . 'phar://../../lib/psx.phar');

require_once('PSX/Bootstrap.php');

spl_autoload_register('PSX_Bootstrap::autoload');


// initialize objects
$http     = new PSX_Http(new PSX_Http_Handler_Curl());
$self     = 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
$cred     = loadCredentials();
$oauth    = new PSX_Oauth($http);
$xrds     = null;

$validate = new PSX_Validate();
$session  = new PSX_Session('news_reader', $validate);


// liddle controller to route all requests. If the GET variable xhr is set we
// have requested the page via XHR else with a browser
if(isset($_GET['xhr']))
{
	try
	{
		if($_GET['xhr'] == 'news')
		{
			echo loadNews();
		}
		else if($_GET['xhr'] == 'comment')
		{
			if(PSX_Base::getRequestMethod() == 'POST')
			{
				$pageId = isset($_POST['page_id']) ? intval($_POST['page_id']) : 0;
				$refId  = isset($_POST['ref_id'])  ? intval($_POST['ref_id'])  : 0;
				$text   = isset($_POST['text']   ) ? $_POST['text']            : '';

				echo postComment($pageId, $refId, $text);
			}
			else
			{
				$newsId = isset($_GET['news_id']) ? intval($_GET['news_id']) : 0;

				echo loadComments($newsId);
			}
		}
		else
		{
			throw new PSX_Exception('Invalid xhr request');
		}
	}
	catch(Exception $e)
	{
		echo PSX_Json::encode(array('success' => false, 'text' => $e->getMessage()));
	}

	exit;
}
else
{
	// discover oauth endpoints
	$requestUri   = discoverServiceUri('http://oauth.net/core/1.0/endpoint/request');
	$authorizeUri = discoverServiceUri('http://oauth.net/core/1.0/endpoint/authorize');
	$accessUri    = discoverServiceUri('http://oauth.net/core/1.0/endpoint/access');

	if(isset($_GET['callback']) && $_GET['callback'] == '1')
	{
		// oauth callback
		$token       = $session->token;
		$tokenSecret = $session->token_secret;
		$verifier    = isset($_GET['oauth_verifier']) ? $_GET['oauth_verifier'] : null;

		if(!empty($token) && !empty($tokenSecret) && !empty($verifier))
		{
			$response = $oauth->accessToken($accessUri, CONSUMER_KEY, CONSUMER_SECRET, $token, $tokenSecret, $verifier);
			$result   = saveCredentials(array('token' => $response->getToken(), 'token_secret' => $response->getTokenSecret()));

			if($result)
			{
				header('Location: ' . $_SERVER['PHP_SELF']);
				exit;
			}
			else
			{
				throw new PSX_Exception('Could not save credentials');
			}
		}
		else
		{
			throw new PSX_Exception('No token set');
		}
	}
	else if(empty($cred))
	{
		// no credentials start authentication
		$callback = $self . '?callback=1';
		$response = $oauth->requestToken($requestUri, CONSUMER_KEY, CONSUMER_SECRET, $method = 'HMAC-SHA1', $callback);

		$session->set('token', $response->getToken());
		$session->set('token_secret', $response->getTokenSecret());

		$oauth->userAuthorization($authorizeUri, array('oauth_token' => $response->getToken()));
	}
}

/**
 * Tries to discover an XRDS document of the BASE_URL. If the discovery was
 * successful the document is written to the file XRDS_CACHE. Next time the
 * cached version is used. The cached XRDS document doesnt expire if you want to
 * refetch the XRDS simply delete the file
 *
 * @param string $type
 * @return string|null
 */
function discoverServiceUri($type)
{
	global $http, $xrds;

	if(!$xrds instanceof PSX_Xrd)
	{
		$yadis = new PSX_Yadis($http);

		if(is_file(XRDS_CACHE))
		{
			$xrds = file_get_contents(XRDS_CACHE);
		}

		if(!$xrds instanceof PSX_Xrd)
		{
			$xrds = $yadis->discover(new PSX_Url(BASE_URL), true);

			file_put_contents(XRDS_CACHE, $xrds);
		}

		$xrds = $yadis->parse($xrds);
	}

	foreach($xrds->service as $service)
	{
		if(in_array($type, $service->getType()))
		{
			return new PSX_Url($service->getUri());
		}
	}

	return null;
}

/**
 * Discovers the news service and requests the latest news if the service is
 * available. Redirects the complete json result to the browser so we can
 * proccess it on the javascript side.
 *
 * @return string
 */
function loadNews()
{
	global $http, $oauth, $cred;

	$uri = discoverServiceUri('http://ns.amun-project.org/2011/amun/service/news');

	if($uri instanceof PSX_Url)
	{
		$header = array(

			'Accept'        => 'application/json',
			'Authorization' => $oauth->getAuthorizationHeader($uri, CONSUMER_KEY, CONSUMER_SECRET, $cred['token'], $cred['token_secret']),

		);

		$request  = new PSX_Http_GetRequest($uri, $header);
		$response = $http->request($request);

		if($response->getCode() == 200)
		{
			return $response->getBody();
		}
		else
		{
			throw new PSX_Exception('Invalid response code ' . $response->getCode());
		}
	}
	else
	{
		throw new PSX_Exception('Could not found news service');
	}
}

/**
 * Discovers the comment service and requests the latest comments refering to
 * the given $newsId if the service is available. Redirects the complete json
 * result to the browser so we can proccess it on the javascript side.
 *
 * @param integer $newsId
 * @return string
 */
function loadComments($newsId)
{
	global $http, $oauth, $cred;

	$uri = discoverServiceUri('http://ns.amun-project.org/2011/amun/service/comment');

	if($uri instanceof PSX_Url)
	{
		$uri->addParam('fields', 'id,text,date,authorName,authorProfileUrl,authorThumbnailUrl');
		$uri->addParam('filterBy', 'refId');
		$uri->addParam('filterOp', 'equals');
		$uri->addParam('filterValue', $newsId);

		$header = array(

			'Accept'        => 'application/json',
			'Authorization' => $oauth->getAuthorizationHeader($uri, CONSUMER_KEY, CONSUMER_SECRET, $cred['token'], $cred['token_secret']),

		);

		$request  = new PSX_Http_GetRequest($uri, $header);
		$response = $http->request($request);

		if($response->getCode() == 200)
		{
			return $response->getBody();
		}
		else
		{
			throw new PSX_Exception('Invalid response code ' . $response->getCode());
		}
	}
	else
	{
		throw new PSX_Exception('Could not found comment service');
	}
}

/**
 * Sends an post request to the comment service to create a comment. Redirects
 * the complete json response to the browser so we can proccess it on the
 * javascript side.
 *
 * @return string
 */
function postComment($pageId, $refId, $text)
{
	global $http, $oauth, $cred;

	$uri = discoverServiceUri('http://ns.amun-project.org/2011/amun/service/comment');

	if($uri instanceof PSX_Url)
	{
		$body   = array('pageId' => $pageId, 'refId' => $refId, 'text' => $text);
		$header = array(

			'Accept'        => 'application/json',
			'Authorization' => $oauth->getAuthorizationHeader($uri, CONSUMER_KEY, CONSUMER_SECRET, $cred['token'], $cred['token_secret'], 'HMAC-SHA1', 'POST'),

		);

		$request  = new PSX_Http_PostRequest($uri, $header, $body);
		$response = $http->request($request);

		if($response->getCode() == 200)
		{
			return $response->getBody();
		}
		else
		{
			throw new PSX_Exception('Invalid response code ' . $response->getCode());
		}
	}
	else
	{
		throw new PSX_Exception('Could not found news service');
	}
}

/**
 * Tries to load the oauth credentials stored in the CONFIG_FILE. If the
 * CONFIG_FILE doesnt exists null is returned
 *
 * @return array
 */
function loadCredentials()
{
	$credentials = null;

	if(is_file(CONFIG_FILE))
	{
		$config = trim(file_get_contents(CONFIG_FILE));

		if(!empty($config))
		{
			$credentials = unserialize($config);
		}
	}

	return $credentials;
}

/**
 * Saves the $data to the CONFIG_FILE and returns the count of written bytes
 *
 * @param mixed $data
 * @return integer
 */
function saveCredentials($data)
{
	return file_put_contents(CONFIG_FILE, serialize($data));
}

?>
<!DOCTYPE>
<html>
<head>
	<title>Newsreader</title>
	<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
	<script type="text/javascript">
	var baseUrl = '<?php echo $_SERVER['PHP_SELF']; ?>';

	function loadNews()
	{
		$.get(baseUrl + '?xhr=news', function(resp){

			resp = JSON.parse(resp);
			var html = '';

			for(var i = 0; i < resp.entry.length; i++)
			{
				html+= '<div class="news_entry">';
				html+= '<h3><a href="#" onclick="loadComments(' + resp.entry[i].pageId + ',' + resp.entry[i].id + ');return false;">' + resp.entry[i].title + '</a></h3>';
				html+= '<small><a href="' + resp.entry[i].authorProfileUrl + '">' + resp.entry[i].authorName + '</a> on ' + resp.entry[i].date + '</small><br />';
				html+= resp.entry[i].text;
				html+= '</div>';
				html+= '<hr />';
			}

			$('#news_entries').html(html);

		});
	}

	function loadComments(pageId, newsId)
	{
		$('#comment_entries').html('<p>Loading comments ..<blink>.</blink></p>');

		$.get(baseUrl + '?xhr=comment&news_id=' + newsId, function(resp){

			resp = JSON.parse(resp);
			var html = '';

			if(resp.entry.length > 0)
			{
				for(var i = 0; i < resp.entry.length; i++)
				{
					html+= '<div class="comment_entry">';
					html+= '<div style="float:right;"><img src="' + resp.entry[i].authorThumbnailUrl + '" /></div>';
					html+= '<small><a href="' + resp.entry[i].authorProfileUrl + '">' + resp.entry[i].authorName + '</a> on ' + resp.entry[i].date + '</small><br />';
					html+= resp.entry[i].text;
					html+= '</div>';
					html+= '<hr />';
				}
			}
			else
			{
				html = '<p>No comments available</p>';
			}

			$('#comment_entries').html(html);

			$('#news_id').val(newsId);
			$('#page_id').val(pageId);
			$('#comment_form').fadeIn();

		});
	}

	$(document).ready(function(){

		loadNews();

		$('#comment').submit(function(){

			var pageId = $('#page_id').val();
			var newsId = $('#news_id').val();
			var text   = $('#text').val();

			$.post(baseUrl + '?xhr=comment', {page_id: pageId, ref_id: newsId, text: text}, function(resp){

				resp = JSON.parse(resp);

				if(typeof(resp.success) != 'undefined' && resp.success)
				{
					loadComments(pageId, newsId);

					$('#text').val('');
				}
				else
				{
					alert('Error: ' + ((typeof(resp.text) != 'undefined') ? resp.text : 'Unknown error'));
				}

			});

			return false;

		});

	});
	</script>
	<style type="text/css">
	body
	{
		font-size:1em;
		color:#222;
		font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;
	}

	h1
	{
		margin:2px;
		padding:2px;
	}

	h1 a
	{
		text-decoration:none;
		color:#222;
	}

	h2
	{
		margin:1px;
		padding:8px;
		background-color:#ccc;
		border-bottom:2px solid #999;
		font-size:0.8em;
	}

	hr
	{
		border:1px solid #ccc;
	}

	.content
	{
		width:800px;
		margin:0 auto;
		border:1px solid #ccc;
		padding:8px;
		border-radius:6px;
	}

	.news
	{
	}

	#news_entries
	{
		height:400px;
		overflow:auto;
		margin:6px;
	}

	.news_entry
	{
	}

	.comment
	{
	}

	#comment_entries
	{
		margin:6px;
	}

	.comment_entry
	{
	}

	.comment_form
	{
	}
	</style>
</head>
<body>

<div class="content">

	<h1><a href="<?php echo $_SERVER['PHP_SELF']; ?>">Newsreader</a></h1>

	<div class="news">
		<h2>News</h2>
		<div id="news_entries">
			<p>Loading news ..<blink>.</blink></p>
		</div>
	</div>

	<div class="comment">
		<h2>Comments</h2>
		<div id="comment_entries">
			<p>No news selected</p>
		</div>
		<div id="comment_form" style="display:none;">
			<form id="comment" method="POST">
				<input type="hidden" name="page_id" id="page_id" value="" />
				<input type="hidden" name="news_id" id="news_id" value="" />
				<p>
					<label for="text">Comment</label><br />
					<textarea name="text" id="text" rows="6" cols="40"></textarea>
				</p>
				<p>
					<input type="submit" value="Submit" />
				</p>
			</form>
		</div>
	</div>

</div>

</body>
</html>
